"""
@author: Leo
@version: 1.0.0
@file: auth.py
@time: 2023/9/11 10:21 PM
"""
import datetime
import re

import jwt
from django.utils import timezone
from jwt import ExpiredSignatureError
from rest_framework.authentication import BaseAuthentication

from lab.common.enums import ErrorCodeEnum
from lab.common.response import AuthenticationFailed, NotFound
from lab.settings import JWT_ALGORITHM, JWT_SECRET_KEY, JWT_REFRESH_SECRET_KEY
from lab.user.models import ExtendedUser, TokenBlacklist


def get_token(request, secret_key, refresh=False):
    token = request.META.get('HTTP_AUTHORIZATION')
    if not token:
        raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)

    token = re.match(r'[bB]earer\s(\w+\.\w+\..+)', token).group(1)

    if TokenBlacklist.objects.filter(token=token).exists():
        raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)
    try:
        token = jwt.decode(token, secret_key, JWT_ALGORITHM)
    except ExpiredSignatureError as why:
        raise AuthenticationFailed(ErrorCodeEnum.TOKEN_EXPIRED.value)
    except Exception as why:
        raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)
    return token


class VipRequired(BaseAuthentication):

    def authenticate(self, request):
        token = get_token(request, JWT_SECRET_KEY)
        exp = token.get('exp', 0)
        if exp < datetime.datetime.now().timestamp():
            setattr(request, 'user', None)
            raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)
        else:
            extended_user_id = token.get('user_id')
            extended_user = ExtendedUser.objects.filter(id=extended_user_id).first()
            if not extended_user:
                raise NotFound(ErrorCodeEnum.USER_DOES_NOT_EXIST.value)
            if timezone.now() >= extended_user.vip_expired_time:  # UTC timezone
                raise AuthenticationFailed(ErrorCodeEnum.USER_VIP_EXPIRED.value)
            if not extended_user.user.is_active:
                raise AuthenticationFailed(ErrorCodeEnum.USER_DISABLED.value)
            return extended_user, token


class LoginRequired(BaseAuthentication):

    def authenticate(self, request):
        token = get_token(request, JWT_SECRET_KEY)
        exp = token.get('exp', 0)
        if exp < datetime.datetime.now().timestamp():
            setattr(request, 'user', None)
            raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)
        else:
            extended_user_id = token.get('user_id')
            extended_user = ExtendedUser.objects.filter(id=extended_user_id).first()
            if not extended_user:
                raise NotFound(ErrorCodeEnum.USER_DOES_NOT_EXIST.value)
            if not extended_user.user.is_active:
                raise AuthenticationFailed(ErrorCodeEnum.USER_DISABLED.value)
            return extended_user, token


class LoginOptionally(BaseAuthentication):

    def authenticate(self, request):
        try:
            return LoginRequired().authenticate(request)
        except Exception as why:
            return None, None


class RefreshTokenRequired(BaseAuthentication):

    def authenticate(self, request):
        token = get_token(request, JWT_REFRESH_SECRET_KEY)
        exp = token.get('exp', 0)
        if exp < datetime.datetime.now().timestamp():
            setattr(request, 'user', None)
            raise AuthenticationFailed(ErrorCodeEnum.INVALID_TOKEN.value)
        else:
            extended_user_id = token.get('user_id')
            extended_user = ExtendedUser.objects.filter(id=extended_user_id).first()
            if not extended_user:
                raise NotFound(ErrorCodeEnum.USER_DOES_NOT_EXIST.value)
            if not extended_user.user.is_active:
                raise AuthenticationFailed(ErrorCodeEnum.USER_DISABLED.value)
            return extended_user, token
